# pylint: disable-msg=E1101
"""
@author: Sergiy Tsymbalyuk
"""

import logging, os, shutil, re
from datetime import datetime
from hashlib import md5

from pylons import tmpl_context as c, request, url, config
from pylons.i18n import lazy_ugettext as _
from pylons.controllers.util import redirect

from paste.fileapp import FileApp
from repoze.what.plugins.pylonshq import ActionProtector
from repoze.what.predicates import has_permission, not_anonymous
from formalchemy.validators import required
from sqlalchemy import func

from chowa.lib.site.navigation import Tab
from chowa.lib.site.forms import TabularFieldSet
from chowa.model.auth import User

from catalogue.lib.base import BaseController, render, Session
from catalogue.model.catalog import *

log = logging.getLogger(__name__)


_get_doc_type = lambda type_id: Session.query(DocumentType).get(type_id)


def document_exist(fs):
    # TODO: complete all conditions
    if not fs.type_id.value: return None
    doc_type = _get_doc_type(fs.type_id.value)
    query = Session.query(Document)
    query = query.filter_by(type_id=fs.type_id.value,
                            institution_id=fs.institution_id.value)
    if doc_type.has_number:
        query = query.filter_by(number=fs.number.value)
    else:
        query = query.filter_by(title=fs.title.value)
    query = query.filter(func.year(Document.approved) == fs.approved.value.year)
    if query.count():
        return query.one()
    return None


def configure_form(fs, validate=False):
    number = fs.number
    if validate:
        if fs.type_id.value:
            doc_type = _get_doc_type(fs.type_id.value)
            if doc_type.has_number:
                number = number.validate(required)
    fs.configure(include=[fs.title,
                          fs.type,
                          fs.institution,
                          number,
                          fs.approved])


class DocumentController(BaseController):
    __primary_tabs__ = [
        [
            Tab(params=dict(action='view'),
                caption=_('View'),
                description=_('View')
            ),
            Tab(
                params=dict(action='edit'),
                caption=_('Edit'),
                description=_('Edit')
            ),
            Tab(
                params=dict(action='bind_sections'),
                caption=_('Sections')
            ),
            Tab(
                params=dict(action='bind_tags'),
                caption=_('Tags')
            ),
            Tab(
                params=dict(action='attach_files'),
                caption=_('Files')
            ),
        ]
    ]
    
    def view(self, id):
        """View document details."""
        c.document = Session.query(Document).get(id)
        return render('/catalogue/document/view.html')
    
    @ActionProtector(has_permission('catalogue.add.document'))
    def add(self):
        """Post new document."""
        c.document = None
        fs = TabularFieldSet(Document, Session)
        configure_form(fs)
        if request.method == 'POST':
            fs.rebind(data=request.params)
            configure_form(fs, validate=True)
            fs.model.created = datetime.now()
            fs.model.modified = datetime.now()
            author = request.environ.get('repoze.who.identity')['user']
            fs.model.author_id = author.id
            c.document = document_exist(fs)
            if fs.validate() and not c.document:
                fs.sync()
                Session.commit()
                if request.POST.get('next'):
                    return redirect(url.current(action='bind_sections', id=fs.model.id))
                return redirect(url.current(action='view', id=fs.model.id))
        c.fieldset = fs
        return render('/catalogue/document/add.html')
    
    @ActionProtector(has_permission('catalogue.edit.document'))
    def edit(self, id):
        """Change given document."""
        c.document = Session.query(Document).get(id)
        fs = TabularFieldSet(c.document)
        configure_form(fs)
        if request.method == 'POST':
            fs.rebind(data=request.params)
            configure_form(fs, validate=True)
            fs.model.modified = datetime.now()
            if fs.validate():
                fs.sync()
                Session.commit()
                if request.POST.get('next'):
                    return redirect(url.current(action='bind_sections'))
                return redirect(url.current(action='view'))
        c.fieldset = fs
        return render('/catalogue/document/edit.html')
    
    @ActionProtector(has_permission('catalogue.edit.document'))
    def bind_sections(self, id):
        c.document = Session.query(Document).get(id)
        
        user = request.environ.get('repoze.who.identity')['user']
        query = Session.query(Section)
        query = query.join(user_sections)
        query = query.join(User)
        c.sections = query.filter(User.id == user.id).all()
        if request.method == 'POST':
            if request.POST.get('back'):
                return redirect(url.current(action='edit'))
            if request.POST.get('save') or request.POST.get('next'):
                section_ids = [v[8:] for v in request.POST if v[:8] == 'sections']
                query = document_sections.delete(document_sections.c.document_id == id)
                query = query.where(document_sections.c.section_id.in_([section.id for section in c.sections]))
                Session.execute(query)
                for section_id in section_ids:
                    query = document_sections.insert(values=dict(document_id=id,
                                                                 section_id=section_id))
                    Session.execute(query)
                if request.POST.get('next'):
                    return redirect(url.current(action='bind_tags'))
                return redirect(url.current(action='view'))
        return render('/catalogue/document/bind_sections.html')
    
    @ActionProtector(has_permission('catalogue.edit.document'))
    def bind_tags(self, id):
        c.document = Session.query(Document).get(id)
        
        c.tags = Session.query(Tag).all()
        if request.method == 'POST':
            if request.POST.get('back'):
                return redirect(url.current(action='bind_sections'))
            if request.POST.get('save') or request.POST.get('next'):
                tag_ids = [v[4:] for v in request.POST if v[:4] == 'tags']
                query = document_tags.delete(document_tags.c.document_id == id)
                Session.execute(query)
                for tag_id in tag_ids:
                    query = document_tags.insert(values=dict(document_id=id,
                                                             tag_id=tag_id))
                    Session.execute(query)
                if request.POST.get('next'):
                    return redirect(url.current(action='attach_files'))
                return redirect(url.current(action='view'))
        return render('/catalogue/document/bind_tags.html')
    
    @ActionProtector(has_permission('catalogue.edit.document'))
    def attach_files(self, id):
        c.document = Session.query(Document).get(id)
        
        if request.method == 'POST':
            if request.POST.get('back'):
                return redirect(url.current(action='bind_tags'))
            if request.POST.get('save'):
                dst_path = os.path.join(config['app_conf']['documents_dir'], str(id))
                # Delete files
                file_ids = [v[2:] for v in request.POST if v[:2] == 'id']
                for file_id in file_ids:
                    query = Session.query(File).filter_by(id=file_id)
                    removal_file = query.one()
                    try:
                        os.remove(os.path.join(dst_path, removal_file.stored_name))
                    except:
                        pass
                    query.delete(False)
                # Attach new files
                for i in range(1, 6):
                    src_file = request.POST.get('file%s' % i)
                    if not src_file == '':
                        if not os.path.exists(dst_path):
                            os.mkdir(dst_path)
                        dst_file_base, dst_file_ext = os.path.splitext(src_file.filename)
                        dst_filename = md5(dst_file_base.encode('utf-16')).hexdigest() + dst_file_ext
                        dst_file = open(os.path.join(dst_path, dst_filename), 'wb')
                        try:
                            shutil.copyfileobj(src_file.file, dst_file)
                            
                            # Get file basename:
                            # - for windows clients
                            src_filename = src_file.filename.split('\\')[-1]
                            # - for *nix clients
                            src_filename = src_filename.split('/')[-1]
                            
                            db_file = File(original_name=src_filename,
                                           stored_name=dst_filename,
                                           document_id=id)
                            Session.add(db_file)
                        except:
                            pass
                        src_file.file.close()
                        dst_file.close()
                Session.commit()
                return redirect(url.current(action='view'))
        return render('/catalogue/document/attach_files.html')
    
    @ActionProtector(has_permission('catalogue.delete.document'))
    def delete(self, id):
        """Delete given document."""
        if request.method == 'POST':
            if 'yes' in request.POST:
                doc = Session.query(Document).get(id)
                Session.delete(doc)
                Session.commit()
            redirect(request.POST.get('referer'))
        return render('/catalogue/document/delete.html')
    
    @ActionProtector(not_anonymous())
    def favorite(self, id):
        user = request.environ.get('repoze.who.identity')['user']
        uf = user_favorites
        query = uf.select(uf.c.user_id==user.id).where(uf.c.document_id==id)
        is_favor = Session.execute(query).rowcount
        if is_favor:
            query = uf.delete(uf.c.user_id==user.id).where(uf.c.document_id==id)
            Session.execute(query)
            return 'del'
        else:
            query = user_favorites.insert()
            query = query.values(user_id=user.id, document_id=id)
            try:
                Session.execute(query)
                return 'ins'
            except:
                return 'bad ins'
    
    def file(self, id):
        file = Session.query(File).get(id)
        
        file_path = os.path.join(config['app_conf']['documents_dir'],
                                 str(file.document_id),
                                 file.stored_name)
        # FIXME: work incorrect with other encodings
        charset = 'cp1251'
        if re.match('opera', request.user_agent.lower()):
            charset = 'utf8'
        headers = [('Content-Disposition',
                    'attachment; filename=%s' % file.original_name.encode(charset).replace(' ', '_'))]
        fapp = FileApp(file_path, headers=headers)
        return fapp(request.environ, self.start_response)
